// Add trim() to the global string object.
String.prototype.trim = function() {
    return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
};

var ruleManager;
var conflictManager;

var DEBUGJS = false;

/* HELPING STUFF */

/*
** Remove NewLine, CarriageReturn and Tab characters from a String
**   s  string to be processed
** returns new string
*/
function removeNL(s) {
	var r = "";
	for(var i=0; i < s.length; i++) 
	{
		if(s.charAt(i) != '\n' &&
			s.charAt(i) != '\r' &&
			s.charAt(i) != '\t') 
		{
				r += s.charAt(i);
		}
	}
	return r;
}

/* STYLESHEET MANAGEMENT */

// Manage style sheet file related functions
function styleSheetManager()
{
	// Creates a style class definition content ("name=value;name=value;") from an array of StyleProperty objects.
	this.createCssText = function(properties)
	{
		var cssText = "";
		
		for(var i = 0; i < properties.length; i++)
		{
			// IE hack for border-spacing:
			if(properties[i].name == "border-spacing" && properties[i].value && String(properties[i].value) != "")
			{
				// Extra rule!
				cssText += properties[i].name + ": expression(cellSpacing=" + properties[i].value.replace(/^([0-9]+).*$/, "$1") + ");\n";
			}
			
			if(properties[i].value && String(properties[i].value) != "")
			{
				cssText += properties[i].name + ": " + properties[i].value + properties[i].unit + ";\n";
			}
		}
		return cssText;
	}
	
	// Display all selectors from a css file in alert window for debugging.
	this.debugSelectors = function(cssFile)
	{
		var cssRules;
		
		cssRules = cssFile.cssRules || cssFile.rules; // IE
		
		var tmp = "";
		for(var i = 0; i < cssRules.length; i++)
		{
			tmp += cssRules[i].selectorText + "\n";
		}
		alert(tmp);
	}
}

// This class can find, create and set css text contents of various rules in a loaded CSS file.
// Usage: see Modules/StyleSheet/StyleSheetTableScript.js

function StyleRuleManager(ownerDocument, cssFileTitle)
{
	var ownerDocument;
	var cssFileTitle;
	var searchRules;
	var cssFile;
	var cssRules;
	
	/* CONSTRUCTION */

	this.ownerDocument = ownerDocument;
	this.cssFileTitle = cssFileTitle;
	this.searchRules = new Array();
	
	this.init = function()
	{	
		this.cssFile = null;
		this.cssRules = null;
		
		// Find "wysiwyg.css" in the iframe's stylesheets
		for(i = 0; i < this.ownerDocument.styleSheets.length; i++)
		{
			if(this.ownerDocument.styleSheets[i].title == this.cssFileTitle)
			{
				this.cssFile = ownerDocument.styleSheets[i];
			}
		}
		
		// File found, find the requested table style's nodes
		if(this.cssFile)
		{
			this.cssRules = this.cssFile.cssRules ||  this.cssFile.rules; // IE
		}
		else
		{
			alert("Cannot find " + this.cssFileTitle + ".css!");
		}
	}
	
	this.init();
	
	/* PUBLIC METHODS */
	
	// Add a new search criteria object to search[] array.
	// Returns the index of the added search.
	this.addSearch = function(search)
	{
		this.searchRules[this.searchRules.length] = search;
		return this.searchRules.length - 1;
	}
	
	// Search rules by selector text and fill the rules array.
	// After returning, the related rule and ruleIndex values will be filled.
	// Missing rules may be created.
	this.search = function(createMissing)
	{
		var all = "";

		// Loop on all rules in file and match to searched ones.
		for(var i = 0; i < this.cssRules.length; i++)
		{
			//if(rule.type == CSSRule.STYLE_RULE) // const=1 // Removed: IE does not know .type
			
			var fileSelectors = splitCssSelector(this.cssRules[i].selectorText);
			
			// Loop on searched rules: do we need this rule?
			for(var j = 0; j < this.searchRules.length; j++)
			{
				// Main selector matches? 	// IE returns "TABLE.pricelist TD": need lowercase
				if(compareSelectors(this.searchRules[j].selectors[0], fileSelectors[0]))
				{
					// Store rule and increment "i", if more than one rules were "eaten".
					i = this.storeRule(i, j);
				}
			}
		}

		//this.debugSearchResults();
		
		// Create not found rules if necessary
		if(createMissing)
		{
			this.createMissingRules();
		}
		//this.debugSearchResults();
		 
		//var sm = new styleSheetManager();
		//sm.debugSelectors(this.cssFile);
	}
	
	// Update cssText contents belonging to one searched rule.
	this.updateCss = function(ruleIndex, cssText)
	{
		this.searchRules[ruleIndex].updateCss(cssText);
	}
	
	// Returns a pointer to the style object of a search rule 
	// (allows getting the 0th element only, because further coming rules are the same. 
	// You may only READ from the returned object, because updating will require updateCss() to update secondary rules)
	this.getStyle0 = function(ruleIndex)
	{
		return this.searchRules[ruleIndex].rules[0].style;
	}
	
	/* PRIVATE METHODS */
	
	// Add a new rule to the specified CSS file. Returns the added rule's index in cssRules collection.
	this.insertRule = function(selector, style, index)
	{
		if(!this.cssFile.insertRule)
		{
			this.cssFile.addRule(selector, style, index); // IE
		}
		else
		{
			this.cssFile.insertRule(selector + " " + style, index);
		}
		return index;
	}

	// Store a rule's details in the search array.
	// iFileRule: index to the matching rule in the file
	// iSearchRule: to which search to store.
	//
	// Returns the index of the next processable rule. Rules before the returned index must be 
	// ignored by the caller, because they were "eaten" by this function!
	//
	// Besides storing the main rule in rules[0] of the search, if there are more
	// selectors in the search, it will fill them either of:
	// - pointer to the same rule in the file, if there are matching comma separated secondard selectors listed (FF)
	// - pointer to the following rules in the file, if they match the searched rules in exact order (IE)
	// The two methods cannot be mixed. I.e. either the rules are together, or the rules are exploded!
	this.storeRule = function(iFileRule, iSearchRule)
	{
		var searchRule = this.searchRules[iSearchRule];
		var fileRule = this.cssRules[iFileRule];
		
		// Store in the main rule.
		searchRule.rules[0] = fileRule;
		searchRule.ruleIndex[0] = iFileRule;
		
		// Process a,b,c from the file (FF format)
		
		// If file contains more selectors for this rule, store them if they match seletors in the same search
		var fileSelectors = splitCssSelector(fileRule.selectorText);
		
		// Loop all selectors specified in the file (skip stored 0th)
		for(var i = 1; i < fileSelectors.length; i++)
		{
			// Loop all selectors in the search (skip stored 0th)
			for(var j = 1; j < searchRule.selectors.length; j++)
			{
				if(compareSelectors(fileSelectors[i], searchRule.selectors[j]))
				{
					searchRule.rules[j] = fileRule;
					searchRule.ruleIndex[j] = iFileRule;
				}
			}
		}
		
		// Process exploded rules a{} b{} c{} (IE format)
		
		var searchMore = true;
		var more = 1;
		do
		{
			if(this.cssRules.length - 1 < iFileRule + more)
			{
				// end of file
				searchMore = false;
			}
			else if(searchRule.selectors.length - 1 < more)
			{
				// no more selectors for this search
				searchMore = false;
			}
			else 
			{
				// Get next rule's selectors, but work only with the first selector from this rule!(!)
				var fileSelectors = splitCssSelector(this.cssRules[iFileRule + more].selectorText);
				
				if(compareSelectors(fileSelectors[0], searchRule.selectors[more]))
				{
					// Next search selector matches next selector in file
					// If contents also match, store it.
					if(this.cssRules[iFileRule].style.cssText == this.cssRules[iFileRule + more].style.cssText)
					{
						searchRule.rules[more] = this.cssRules[iFileRule + more];
						searchRule.ruleIndex[more] = iFileRule + more;
						more++;
					}
					else
					{
						//Different rule contents, stop.
						searchMore = false;
					}
				}
				else
				{
					// Next selector in file is not our next selector in search
					searchMore = false;
				}
			}
			
		} while(searchMore);
		
		// Skip processed following rules.
		return iFileRule + more - 1;
	}

	// Creates the rules not yet added to searchRules in DOM (and adds to searchRules of course)
	this.createMissingRules = function()
	{
		// Loop on searched rules
		for(var i = 0; i < this.searchRules.length; i++)
		{
			//Loop all selectors in the current search
			for(var j = 0; j < this.searchRules[i].selectors.length; j++)
			{
				// Searched main rule was not found in stylesheet?
				if(!this.searchRules[i].rules[j])
				{
					this.createMissing(i, j);
				}
			}
		}
	}
	
	// Create an empty new rule for a given search rule's given selector.
	this.createMissing = function(searchIndex, selectorIndex)
	{
		// Find a good index for the new item in Css DOM
		var index;
		
		// Creating first searched rule's first selector?
		if(searchIndex == 0 && selectorIndex == 0)
		{
			if(this.searchRules.length == 1)
			{
				// Working with 1 rule, just add to the end.
				index = this.cssRules.length;
			}
			else
			{
				// If first rule missing, find any found rules to insert before
				var findIndex = 1;
				var found = false;
				do
				{
					// This search has the main selector found? (There is no way that only sub-selector was found!)
					if(this.searchRules[findIndex].rules[0])
					{
						// Will insert before the first found rule's first rule.
						index = this.searchRules[findIndex].ruleIndex[0];
						found = true;
					}
					findIndex++;
				}
				while(!found && findIndex < this.searchRules.length);

				// Not found any rules, start inserting at end.
				if(!found)
				{
					index = this.cssRules.length;
				}
			}
		}
		else if(selectorIndex > 0)
		{
			// A sub rule of the current (incl. first) rule is missing
			index = this.searchRules[searchIndex].ruleIndex[selectorIndex - 1 ] + 1;
		}
		else
		{
			// A main rule (not first) is missing.
			// Get the previous search's last index and add 1 for new insert point
			index = this.searchRules[searchIndex - 1].getLastIndex() + 1;
		}
	
		// Create empty rule the missing search rule selector in the StyleSheet DOM
		var missingSearch = this.searchRules[searchIndex];
		var insertCount = 0;

		if(!missingSearch.rules[selectorIndex])
		{
			missingSearch.ruleIndex[selectorIndex] = this.insertRule(missingSearch.selectors[selectorIndex], "{}", index);
			missingSearch.rules[selectorIndex] = this.cssRules[missingSearch.ruleIndex[selectorIndex]];
			insertCount++;
		}
		
		// Later indexes must be increased to reflect the insertion
		for(var i = searchIndex + 1; i < this.searchRules.length; i++)
		{
			this.searchRules[i].increaseIndex(insertCount);
		}
	}
	
	this.debugSearchResults = function()
	{
		var tmp = "";
		for(var j = 0; j < this.searchRules.length; j++)
		{
			tmp += "MAIN\n";
			for(var k = 0; k < this.searchRules[j].selectors.length; k ++)
			{
				if(this.searchRules[j].rules[k])
				{
					tmp += this.searchRules[j].selectors[k] + "=" + this.searchRules[j].rules[k].selectorText + "\n";
				}
				else
				{
					tmp += this.searchRules[j].selectors[k] + "=" + this.searchRules[j].rules[k] + "\n";
				}
			}
		}
		alert(tmp);
	}
}

// Contains one search criteria and stores results for styleSheetManager.searchStyleRules()
function StyleRuleSearch(selector) 
{
	this.selectors = new Array();
	this.rules = new Array();
	this.ruleIndex = new Array();
	
	this.selectors = splitCssSelector(selector);
	
	// Return the index of our last rule (used for inserting after).
	this.getLastIndex = function()
	{
		return this.ruleIndex[this.ruleIndex.length - 1];
	}
	
	// Increase all indices by an amount (used when a rule is inserted before)
	this.increaseIndex = function(amount)
	{
		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.rules[i])
			{
				this.ruleIndex[i] += amount;
			}
		}
	}
	
	// Update CSS properties of all contained rules
	this.updateCss = function(cssText)
	{
		this.rules[0].style.cssText = cssText;
		for(var i = 1; i < this.rules.length; i++)
		{
			if(this.rules[i] != this.rules[0])
			{	
				this.rules[i].style.cssText = cssText;
			}
		}
	}
}

// Split a css selector by commas and return as lowercased, trimmed array.	
function splitCssSelector(selector)
{
	var selectors = selector.split(','); 
	
	for(var s = 0; s < selectors.length; s++)
	{
		selectors[s] = selectors[s].trim();
	}
	return selectors;
}

// Compare two css selectors. Currently just toLowerCase for IE's TABLE.pricelist TD, but later
// it should lowercase only NODE NAMES, not class names!
// Returns true if selectors match, false if not.
function compareSelectors(selector1, selector2)
{
	return (selector1.toLowerCase() == selector2.toLowerCase());
}

// Contains one css class property (name/value/unit) for styleSheetManager.createCssText()
function StyleProperty(initName, initValue, initUnit)
{
	this.name = initName;
	this.value = initValue;
	this.unit = initUnit;
}

/* STYLESHEET DESIGNER MODULES */

// Returns the last selected task
function getTask()
{
	return document.getElementById("hdnTask").value;
}

// Stores task and calls taskChanged() of the current style editor page
function setTask(task)
{
	document.getElementById("hdnTask").value = task;
	taskChanged();
}

// Return the currently edited style's name from hidden field.
function getStyleName()
{
	return document.getElementById('hdnOriginalName').value;
}

// Return the currently edited style's index when editing multiple styles at once (heading, font..)
function getStyle()
{
	return Number(document.getElementById('ddlStyle')[document.getElementById('ddlStyle').selectedIndex].value);
}


// Class holds all rules and other objects related to the currently running desinger page.
function stylesDesigner()
{
	var rules; // Array for designerRules objects
	var ruleManager; // style rule manager for working with wysiwyg.css
	
	this.rules = new Array();
	
	// Initialize designer:
	// - Pass the preview frame's document and the css file name to search for.
	// - Parse loaded cssTextIn input values to propertyManagers
	// - find related Css classes in the DOM
	this.init = function(doc, cssFile)
	{
		// Find the related rules in wysiwyg.css
		// Creates empty CSSStyleRule nodes for missing ones, maintains correct order when creating.
		this.ruleManager = new StyleRuleManager(doc, cssFile);

		for(var i = 0; i < this.rules.length; i++)
		{
			this.ruleManager.addSearch(new StyleRuleSearch(this.rules[i].selector));
		}
		this.ruleManager.search(true);
		
		// Parse all our rule's css text input into PropertyManagers
		for(var i = 0; i < this.rules.length; i++)
		{
			this.rules[i].parseCssTextIn();
		}
	}
	
	this.generateOutput = function()
	{
		// Let all our rules create their set of StyleRules.
		for(var i = 0; i < this.rules.length; i++)
		{
			this.rules[i].generateOutput();
		}
	}
	
	// Update loaded preview css file's dom with the current rules.
	this.updateCssDom = function()
	{
		for(var i = 0; i < this.rules.length; i++)
		{
			this.ruleManager.updateCss(i, this.rules[i].cssTextOut);
		}
	}
	
	// Return all rules as complete CSS file snipset.
	this.getCss = function()
	{
		var css = "";
		
		for(var i = 0; i < this.rules.length; i++)
		{
			css += this.rules[i].selector + "\n{\n" + this.rules[i].cssTextOut + "}\n\n";
		}
		
		return css;
	}
}

// Class to store all properties of one css style rule in stylesDesigner class.
function designerRule()
{
	var selector; // Input of original selector of this rule
	var cssTextIn;  // Input of original css contents
	
	//var type;	  // The selected "type" on the GUI (e.g. custom, noborder...)
	var properties; // propertyManager for known properties in this rule
	var cssManager;	// Helper class for managing css dom.
	
	var output;		// Output, array of StyleProperty objects.
	var cssTextOut; // Output in css text format.
	
	this.properties = new propertyManager();
	this.cssManager = new styleSheetManager();
	//this.type = 'custom';
	
	// Parse the loaded cssText property into the properties(Manager).
	// "properties" must be initialized before, because only the "allowed" (known) properties will be stored.
	this.parseCssTextIn = function() 
	{
		this.properties.loadFromCss(explodeStyle(this.cssTextIn));	
	}
	
	// Create StyleProperty objects from the defined properties. And create cssTextOut, the same in plain text.
	this.generateOutput = function()
	{
		this.output = new Array();
		
		// Extend output array with all necessary properties
		this.properties.createTableRules(this.output);
		this.properties.createBoxRules(this.output);
		this.properties.createBorderRules(this.output);
		this.properties.createCellPropRules(this.output);
		this.properties.createMarginRules(this.output);
		this.properties.createBackgroundRules(this.output);
		this.properties.createFontRules(this.output);
		
		this.cssTextOut = this.cssManager.createCssText(this.output);
	}
	
	// Return a style property's current value.
	this.getProperty = function(propertyName)
	{
		return this.properties.getProperty(propertyName);
	}

	// Sets a style property's current value.
	this.setProperty = function(propertyName, value)
	{
		return this.properties.setProperty(propertyName, value);
	}
	
	// Returns true if a property is available and set to not empty value.
	this.isSet = function(propertyName)
	{
		return this.properties.isSet(propertyName);
	}
}


// This manages the collection of possible properties in one designerRule object.
propertyManager = function()
{
	var prop;
	this.prop = new Array();
	
	// Return a style property's current value.
	this.getProperty = function(propertyName)
	{
		return this.prop[propertyName];
	}
	
	// Sets a style property's current value. (Ignores not allowed properties.)
	this.setProperty = function(propertyName, value)
	{
		if(this.has(propertyName))
		{
			this.prop[propertyName] = value;
		}
	}

	// Returns true if a property is available and set to not empty value.
	this.isSet = function(propertyName)
	{
		return (this.getProperty(propertyName) != null && this.getProperty(propertyName) != '');
	}
	
	// Check if the specific element allows a specific css property.
	// e.g has('table', 'border-top-color') -> true (Not if it is set or not, but if there is a possibilty to set!!)
	this.has = function(propertyName)
	{
		if(this.prop[propertyName] != null)
		{
			return true;
		}
		return false;
	}
	
	// Check if the element allows all four sides of a property (stlye2 may be "");
	// e.g. hasAllSides('border', 'width') (->true) or hasAllSides(  'padding') (->false); 
	// Returns false if not all the four sides are allowed (or no this kind of sides at all).
	this.hasAllSides = function(style1, style2)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		if(this.has(style1 + "-top" + style2) &&
			this.has(style1 + "-right" + style2) &&
			this.has(style1 + "-bottom" + style2) &&
			this.has(style1 + "-left" + style2))
		{
			return true;
		}
		return false;
	}

	// Check if a property is the same for the 4 sides
	// e.g. sameSides('border', 'width') or sameSides('padding', '')
	this.sameSides = function(style1, style2)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		var topValue = this.prop[style1 + "-top" + style2].toLowerCase();
		
		if(this.prop[style1 + "-right" + style2].toLowerCase() == topValue &&
			this.prop[style1 + "-bottom" + style2].toLowerCase() == topValue &&
			this.prop[style1 + "-left" + style2].toLowerCase() == topValue)
			{
				return true;
			}
			
		return false;
	};

	// Check if the element allows four CORNERS of a property
	// Maybe only this works: hasCorners('border', 'radius')
	// Returns false if not all the four corners are allowed (or no this kind of corners at all).
	this.hasAllCorners = function(style1, style2)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		if(this.has(style1 + "-top-left" + style2) &&
			this.has(style1 + "-top-right" + style2) &&
			this.has(style1 + "-bottom-left" + style2) &&
			this.has(style1 + "-bottom-right" + style2))
		{
			return true;
		}
		return false;
	}

	// Check if a property is the same for the 4 corners
	// Maybe only this works: sameCorners('border', 'radius')
	this.sameCorners = function(style1, style2)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		var topValue = this.prop[style1 + "-top-left" + style2].toLowerCase();
		
		if(this.prop[style1 + "-top-right" + style2].toLowerCase() == topValue &&
			this.prop[style1 + "-bottom-left" + style2].toLowerCase() == topValue &&
			this.prop[style1 + "-bottom-right" + style2].toLowerCase() == topValue)
			{
				return true;
			}
			
		return false;
	};
	
	// Check four border sides, return the following values
	// "-" : There are no borders at all
	// "b" : (box) Four sides have the same border
	// "v" : (vertical) Left+Right are the same, Top+Bottom have no borders
	// "h" : (horizontal) Top+Bottom are the same, Left+Right have no borders
	// "c" : (custom) None of the above patterns match, borders vary
	this.compareBorders = function()
	{
		// Anything specfiied for that side?
		var isTop  = (this.isSet('border-top-width') || this.isSet('border-top-color') || this.isSet('border-top-style'));
		var isRight = (this.isSet('border-right-width') || this.isSet('border-right-color') || this.isSet('border-right-style'));
		var isBottom = (this.isSet('border-bottom-width') || this.isSet('border-bottom-color') || this.isSet('border-bottom-style'));
		var isLeft = (this.isSet('border-left-width') || this.isSet('border-left-color') || this.isSet('border-left-style'));

		if(this.sameSides('border', 'width') && this.sameSides('border', 'color') && this.sameSides('border', 'style'))
		{
			// 4 equal sides
			if(this.isSet('border-top-width') || this.isSet('border-top-color') || this.isSet('border-top-style'))
			{
				// top set so all set and same: "box"
				return "b";
			}
			// Same sides and not visible, so "-"
			return "-";
		}
		// not same sides
		
		if(isLeft && isRight && !isTop && !isBottom)
		{
			// Has only vertical, are them equal?
			if(this.getProperty('border-left-color') == this.getProperty('border-right-color') &&
				this.getProperty('border-left-width') == this.getProperty('border-right-width') &&
				this.getProperty('border-left-style') == this.getProperty('border-right-style'))
			{
				return "v";
			}
			return "c";
		}
		
		if(!isLeft && !isRight && isTop && isBottom)
		{
			// Has only top and bottom, are them equal?
			if(this.getProperty('border-top-color') == this.getProperty('border-bottom-color') &&
				this.getProperty('border-top-width') == this.getProperty('border-bottom-width') &&
				this.getProperty('border-top-style') == this.getProperty('border-bottom-style'))
			{
				return "h";
			}
			return "c";
		}
		
		return "c";
	}
	
	// Copy a style property from a side value to the 4 sides.
	// E.g. cloneSides('top', 'border', 'width');
	this.cloneSides = function(side, style1, style2)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		var value = this.prop[style1 + "-" + side + style2];
		
		if(this.has(style1 + "-top" + style2))
		{
			this.prop[style1 + "-top" + style2] = value;
		}
		if(this.has(style1 + "-right" + style2))
		{
			this.prop[style1 + "-right" + style2] = value;
		}
		if(this.has(style1 + "-bottom" + style2))
		{
			this.prop[style1 + "-bottom" + style2] = value;
		}
		if(this.has(style1 + "-left" + style2))
		{
			this.prop[style1 + "-left" + style2] = value;
		}
	};

	// Set 4 border properties to a same value.
	this.setSides = function(style1, style2, value)
	{
		if(style2 != "")
		{
			style2 = "-" + style2;
		}
		if(this.has(style1 + "-top" + style2))
		{
			this.prop[style1 + "-top" + style2] = value;
		}
		if(this.has(style1 + "-right" + style2))
		{
			this.prop[style1 + "-right" + style2] = value;
		}
		if(this.has(style1 + "-bottom" + style2))
		{
			this.prop[style1 + "-bottom" + style2] = value;
		}
		if(this.has(style1 + "-left" + style2))
		{
			this.prop[style1 + "-left" + style2] = value;
		}
	};

	// Checks if border top color/width/style is missing and sets all 4 borders to a default if 
	// there is probably no visible border.
	this.makeDefaultBorder = function(side)
	{
		if(side)
		{
			// Make one side border
			
			// Regex matches 0 or 0px (or similar), or width is just empty?
			if(/^0+[^0-9]*$/.test(this.prop['border-' + side + '-width']) || this.prop['border-' + side + '-width'] == "")
			{
				this.setProperty('border-' + side +  '-width', '1px');
			}
			if(this.prop['border-' + side + '-color'] == "")
			{
				this.setProperty('border-' + side + '-color', '#000000');
			}
			if(this.prop['border-' + side + '-style'] == "none" || this.prop['border-' + side + '-style'] == "")
			{
				this.setProperty('border-' + side + '-style', 'solid');
			}
		}
		else
		{
			// Make four borders.
			
			// Regex matches 0 or 0px (or similar), or width is just empty?
			if(/^0+[^0-9]*$/.test(this.prop['border-top-width']) || this.prop['border-top-width'] == "")
			{
				this.setSides('border', 'width', '1px');
			}
			if(this.prop['border-top-color'] == "")
			{
				this.setSides('border', 'color', '#000000');
			}
			if(this.prop['border-top-style'] == "none" || this.prop['border-top-style'] == "")
			{
				this.setSides('border', 'style', 'solid');
			}
		}
	};
	
	// Set border properties to not set (all if null passed, or a given side otherwise).
	this.removeBorders = function(side)
	{
		if(side)
		{
			// Remove border from 1 side
			this.setProperty('border-' + side + '-width', '');
			this.setProperty('border-' + side + '-color', '');
			this.setProperty('border-' + side + '-style', '');
		}
		else
		{
			// Remove all 4 borders
			this.setSides('border', 'width', '');
			this.setSides('border', 'color', '');
			this.setSides('border', 'style', '');
		}
	};
	
	// Generate StyleProperty objects from border related properties
	// dest: array reference to put StyleProperty objects in.
	this.createBorderRules = function(dest)
	{
		if(this.hasAllSides('border', 'style') && this.sameSides('border', 'style'))
		{
			dest[dest.length] = new StyleProperty("border-style", this.prop['border-top-style'], "");
		}
		else
		{
			this.createListRules(dest, new Array('border-top-style', 
				'border-right-style', 'border-bottom-style', 'border-left-style'));
		}
		
		if(this.hasAllSides('border', 'width') && this.sameSides('border', 'width'))
		{
			dest[dest.length] = new StyleProperty("border-width", this.prop['border-top-width'], "");
		}
		else
		{
			this.createListRules(dest, new Array('border-top-width', 
				'border-right-width', 'border-bottom-width', 'border-left-width'));
		}

		if(this.hasAllSides('border', 'color') && this.sameSides('border', 'color'))
		{
			dest[dest.length] = new StyleProperty("border-color", this.prop['border-top-color'], "");
		}
		else
		{
			this.createListRules(dest, new Array('border-top-color',
				'border-right-color', 'border-bottom-color', 'border-left-color'));
		}
	};

	// Generate StyleProperty objects from font related properties
	// dest: array reference to put StyleProperty objects in.
	this.createFontRules = function(dest)
	{
		// Currently known font properties
		var fontProps = new Array('font-family', 'font-size', 'font-height', 'font-weight', 'font-style', 'color', 'text-indent', 'line-height', 'text-shadow');
		this.createListRules(dest, fontProps);	
	};

	// Generate StyleProperty objects from table related properties
	// dest: array reference to put StyleProperty objects in.
	this.createTableRules = function(dest)
	{
		// Currently known font properties
		var tableProps = new Array('border-collapse', 'border-spacing');
		this.createListRules(dest, tableProps);
	};

	// Generate StyleProperty objects from box related properties
	// dest: array reference to put StyleProperty objects in.
	this.createBoxRules = function(dest)
	{
		// Currently known font properties
		var boxProps = new Array('display', 'float', 'clear');
		this.createListRules(dest, boxProps);
	};
	
	// Generate StyleProperty objects from font related properties
	// dest: array reference to put StyleProperty objects in.
	this.createBackgroundRules = function(dest)
	{
		// Currently known background properties
		var bgProps = new Array('background-image', 'background-repeat', 'background-position', 'background-attachment');
		this.createListRules(dest, bgProps);	
	};


	
	// Generate StyleProperty objects from cell prop related properties
	// dest: array reference to put StyleProperty objects in.
	this.createCellPropRules = function(dest)
	{
		if(this.hasAllSides('padding', '') && this.sameSides('padding', ''))
		{
			dest[dest.length] = new StyleProperty("padding", this.prop['padding-top'], "");
		}
		else
		{
			this.createListRules(dest, new Array('padding-top',
				'padding-right', 'padding-bottom', 'padding-left'));
		}

		if(this.hasAllCorners('border', 'radius') && this.sameCorners('border', 'radius'))
		{
			dest[dest.length] = new StyleProperty("border-radius", this.prop['border-top-left-radius'], "");
		}
		else
		{
			this.createListRules(dest, new Array('border-top-left-radius', 
				'border-top-right-radius', 'border-bottom-left-radius', 'border-bottom-right-radius'));
		}
		// UGLY solution: Makes inner content not flow out when there is a radius!
		if(this.prop['border-top-left-radius'] || this.prop['border-top-right-radius']
			|| this.prop['border-bottom-left-radius'] || this.prop['border-bottom-right-radius'])
		{
			// FIXME: need a better solution, e.g. apply rounding to the H2 too.
			//dest[dest.length] = new StyleProperty("overflow", "hidden", "");
		}
		var props = new Array('background-color', 'text-align', 'vertical-align', 'width', 'min-height', 'box-shadow');
		this.createListRules(dest, props);
	};

	// Generate StyleProperty objects from margin related properties
	// dest: array reference to put StyleProperty objects in.
	this.createMarginRules = function(dest)
	{
		if(this.hasAllSides('margin', '') && this.sameSides('margin', ''))
		{
			dest[dest.length] = new StyleProperty("margin", this.prop['margin-top'], "");
		}
		else
		{
			this.createListRules(dest, new Array('margin-top',
				'margin-right', 'margin-bottom', 'margin-left'));
		}
	};

	// Create a set of rules provided in propertyNames array.
	this.createListRules = function(dest, propertyNames)
	{
		for(var p = 0; p < propertyNames.length; p++)
		{
			if(this.has(propertyNames[p]))
			{
				this.createSingleRule(dest, propertyNames[p]);
			}
		}
	}
	
	// Create a StyleProperty in an output array from one property. E.g. createSingleRule(settings.td, 'td', 'color');
	this.createSingleRule = function(dest, propertyName)
	{
		var i = dest.length;
		dest[i++] = new StyleProperty(propertyName, this.prop[propertyName], "");
	};
	
	// Copy all style properties from an array to a destination. Ignores properties not defined in the destination.
	this.loadFromCss = function(st)
	{
		// Loop on all possible properties for this element
		for(var key in this.prop)
		{
			// Some props might have been 'nullified' by killProperty.
			if(this.has(key)) 
			{
				if(st[key])
				{
					// If property defined in input css, store it.
					this.prop[key] = st[key];
				}
				else
				{
					// If property not defined in input css, clear it.
					this.prop[key] = '';
				}
			}
		}
	};

	// PROPERTY INITIALIZATION

	// Create one single propertie
	this.initProperty = function(propertyName)
	{
		this.prop[propertyName] = '';
	}

	// Disable one single propertie
	this.killProperty = function(propertyName)
	{
		this.prop[propertyName] = null;
	}
	
	// Create border related properties
	this.initBorder = function()
	{
		this.prop['border-top-width'] = '';
		this.prop['border-right-width'] = '';
		this.prop['border-left-width'] = '';
		this.prop['border-bottom-width'] = '';
		this.prop['border-top-color'] = '';
		this.prop['border-right-color'] = '';
		this.prop['border-left-color'] = '';
		this.prop['border-bottom-color'] = '';
		this.prop['border-top-style'] = '';
		this.prop['border-right-style'] = '';
		this.prop['border-left-style'] = '';
		this.prop['border-bottom-style'] = '';
	}
	
	// Create font related properties
	this.initFont = function()
	{
		this.prop['color'] = '';
		this.prop['font-family'] = '';
		this.prop['font-size'] = '';
		this.prop['line-height'] = '';
		this.prop['font-weight'] = '';
		this.prop['font-style'] = '';
		this.prop['text-indent'] = '';
		this.prop['text-shadow'] = '';
	}
	
	// Create cell prop related properties
	this.initCellProp = function()
	{
		this.prop['padding-top'] = '';
		this.prop['padding-right'] = '';
		this.prop['padding-left'] = '';
		this.prop['padding-bottom'] = '';
		this.prop['border-top-left-radius'] = '';
		this.prop['border-top-right-radius'] = '';
		this.prop['border-bottom-left-radius'] = '';
		this.prop['border-bottom-right-radius'] = '';
		this.prop['background-color'] = '';
		this.prop['text-indent'] = '';
		this.prop['text-align'] = '';
		this.prop['vertical-align'] = '';
		this.prop['box-shadow'] = '';
	}

	// Create cell prop related properties
	this.initMargin = function()
	{
		this.prop['margin-top'] = '';
		this.prop['margin-right'] = '';
		this.prop['margin-left'] = '';
		this.prop['margin-bottom'] = '';
	}
	
	this.initBackground = function ()
	{
		this.prop['background-image'] = '';
		this.prop['background-repeat'] = '';
		this.prop['background-position'] = '';
		this.prop['background-attachment'] = '';
	}
		
	// Dump all values to alert box for debugging
	this.dump = function()
	{
		var tmp = "";
		for(var prop in this)
		{
			if (typeof(prop) == 'function')
				continue;
				
			tmp += "[" + prop + "](" + typeof(prop) + ") = " + this.prop[prop] + "\n";
		}
		alert(tmp);
	}
	
}

// Parse a style.cssText string and return as array of name-value pairs.
// Also "explodes" some of the possible shorthands to full values.
function explodeStyle(str) 
{
	var ar = new Array();
	var explodeRx = new RegExp('^(border-width|border-style|border-color|margin|padding)$', 'i');
	var explodeCornerRx = new RegExp('^border-radius$', 'i');

	// Return empty array
	if (str == null)
	{
		return ar;
	}

	// Split css text and fill into 'ar'.
	var st = str.split(';');

	for (var i=0; i<st.length; i++) 
	{
		if (st[i] == '')
		{
			continue;
		}

		var re = new RegExp('^\\s*([^:]*):\\s*(.*)\\s*$');
		var pa = st[i].replace(re, '$1||$2').split('||');
		if (pa.length == 2)
		{
			//ar[pa[0].toLowerCase()] = pa[1];
			var key = pa[0].toLowerCase();
			
			if(explodeRx.test(key))
			{
				// Explodable - sides
				if(key.indexOf("-") > -1) // need "top" in between
				{
					ar[key.replace("-", "-top-")] = pa[1];
					ar[key.replace("-", "-right-")] = pa[1];
					ar[key.replace("-", "-bottom-")] = pa[1];
					ar[key.replace("-", "-left-")] = pa[1];
				}
				else				// need "top" at end
				{
					ar[key + "-top"] = pa[1];
					ar[key + "-right"] = pa[1];
					ar[key + "-bottom"] = pa[1];
					ar[key + "-left"] = pa[1];
				}
			}
			else if(explodeCornerRx.test(key)) 
			{
				// Explodable - corners
				ar[key.replace("-", "-top-left-")] = pa[1];
				ar[key.replace("-", "-top-right-")] = pa[1];
				ar[key.replace("-", "-bottom-left-")] = pa[1];
				ar[key.replace("-", "-bottom-right-")] = pa[1];
			}
			else
			{
				ar[key] = pa[1];
			}
				
		}
	}
	
	return ar;
}

// Load necessary style sheet files into the preview frame by creating LINK tags
function loadPreviewStyles(previewDoc, cssFiles)
{
	var headNodes = previewDoc.getElementsByTagName("head");
	var beforeNode = previewDoc.getElementsByTagName("link")[0]; // Insert before wysiwyg.css link tag
	
	// Array of CSS file URLs
	var styles = cssFiles.split(",");
	var link;

	for(var i = 0; i < styles.length; i++)
	{
		link = previewDoc.createElement("link");
		link.href = styles[i];
		link.rel = "stylesheet";
		link.type = "text/css";
		headNodes[0].insertBefore(link, beforeNode);
	}

	// Extra plain HTML for HEAD (e.g. font service provider's code)
	var headhtml = tinyMCEPopup.editor.getParam("stylelibrary_headhtml", "");
	if(headhtml) {
		jQuery(beforeNode).after(headhtml);
	}
}

// ******* BORDER CONFLICTS ************

var borderPriority = new Array();
borderPriority['double'] = 1;
borderPriority['solid'] = 2;
borderPriority['dashed'] = 3;
borderPriority['dotted'] = 4;
borderPriority['ridge'] = 5;
borderPriority['outset'] = 6;
borderPriority['groove'] = 7;
borderPriority['inset'] = 8;

function borderConflictManager()
{
	this.conflicts = new Array();
	this.pxRx = new RegExp('^([0-9]+)px$');

	this.init = function()
	{
		this.conflicts = new Array();
	}
	
	// Check if a border with gaps is used together with a background color.
	// Return true if there is a conflict added to this.conflicts array.
	this.checkBorderGaps = function(borderRule, backgroundRule, side, errorMsg)
	{
		var style = borderRule.getProperty('border-' + side + '-style');
		var bg = backgroundRule.getProperty('background-color');
		
		if((style == 'dotted' || style == 'dashed' || style == 'double') && bg != '')
		{
			var c = new borderConflict();
			c.type = "gaps";
			c.rule1 = borderRule;
			c.rule2 = backgroundRule;
			c.side1 = side;
			c.errorMsg = errorMsg;
			c.solutions[0] = 'nogaps';
			c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_solid_border');
			c.solutions[1] = 'nobg';
			c.solutionNames[1] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_no_background');
			this.conflicts[this.conflicts.length] = c;
			return true;
		}
		return false;
	}
	
	// Check if outer border is wider than inner border (if their style or color is different)
	// This is how IE "resolves" border conflict: draws outer almost every time. Standard
	// lets "every time" rule only for wider border, thus we must force this.
	this.checkOuterBorder = function(outerRule, innerRule, outerSide, innerSide, errorMsg)
	{
		// Only inner specified?
		if(outerRule.getProperty('border-' + outerSide + '-color') == '')
		{
			if(innerRule.getProperty('border-' + innerSide + '-color') == '') 
			{
				// No border at all
				return;
			}
			else
			{
				// Only inner, not good. 
				// FIXME: borderconflicts are overcomplicated. postponed. [WPS 5.4 OK - Lion - Low priority ]
			}
		}
		
		// Both are specified?
		// If yes, are them different style or different color?
		if(outerRule.getProperty('border-' + outerSide + '-color') != '' &&
			innerRule.getProperty('border-' + innerSide + '-color') != '' &&
			
			(outerRule.getProperty('border-' + outerSide + '-style') == innerRule.getProperty('border-' + innerSide + '-style') &&
			outerRule.getProperty('border-' + outerSide + '-width') == innerRule.getProperty('border-' + innerSide + '-width') &&
			outerRule.getProperty('border-' + outerSide + '-color') == innerRule.getProperty('border-' + innerSide + '-color')))
		{
			// Same border spec or one of borders is not set.
			return;
		}
		
		// Same width? Check if style is stronger outside and allow that
		if(outerRule.getProperty('border-' + outerSide + '-width') == innerRule.getProperty('border-' + innerSide + '-width'))
		{
			var outerStyle = outerRule.getProperty('border-' + outerSide + '-style');
			var innerStyle = innerRule.getProperty('border-' + innerSide + '-style');
			
			// If one is none, that works ok in IE  (This won't happen probably because none sets no color thus returns above)
			if(outerStyle == 'none' || innerStyle == 'none')
			{
				return;
			}
			if(borderPriority[outerStyle] < borderPriority[innerStyle])
			{
				return;
			}
		}
		
		var currentConflicts = this.conflicts.length;
		
		var innerWidth = innerRule.getProperty('border-' + innerSide + '-width');
		var innerStyle = innerRule.getProperty('border-' + innerSide + '-style');
		var outerWidth = outerRule.getProperty('border-' + outerSide + '-width');
		var needSolution = false;
		
		// Only one of them is pixel?
		if(this.pxRx.test(innerWidth) && !this.pxRx.test(outerWidth))
		{
			// Only inner is pixel 
			// Cant compare; always set a value; try to keep outer text value
			var c = new borderConflict();
			c.type = "outerborder";
			c.rule1 = outerRule;
			c.side1 = outerSide;
			c.rule2 = innerRule;
			c.side2 = innerSide;
			c.errorMsg = errorMsg;
		
			if(outerWidth == 'thick') 
			{
				c.solutions[0] = 'setinner';
				c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
				c.solutionParams[0] = 'medium';
			}
			else if(outerWidth == 'medium')
			{
				// Double border must not be thined
				if(innerStyle == 'double')
				{
					c.solutions[0] = 'setboth';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = new Array();
					c.solutionParams[0]['inner'] = 'medium';
					c.solutionParams[0]['outer'] = 'thick';
				}
				else
				{
					c.solutions[0] = 'setinner';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = 'thin';
				}
			}
			else
			{
				// Outer is 'thin', need to change it to inner + 1px
				var innerPx = Number(innerWidth.match(this.pxRx)[1]);
				c.solutions[0] = 'setouter';
				c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
				c.solutionParams[0] = (innerPx + 1) + 'px';
			}
			c.errorMsg += " " + tinyMCEPopup.editor.getLang('stylelibrary_dlg.conflict_outer_cant_compare');
			
			this.conflicts[this.conflicts.length] = c;
		}
		else if(!this.pxRx.test(innerWidth) && this.pxRx.test(outerWidth))
		{
			// Only outer is pixel
			// Cant compare; always set a value; try to keep outer text value
			var c = new borderConflict();
			c.type = "outerborder";
			c.rule1 = outerRule;
			c.side1 = outerSide;
			c.rule2 = innerRule;
			c.side2 = innerSide;
			c.errorMsg = errorMsg;
		
			if(innerWidth == 'thin') 
			{
				c.solutions[0] = 'setouter';
				c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
				c.solutionParams[0] = 'medium';
			}
			else if(innerWidth == 'medium')
			{
				c.solutions[0] = 'setouter';
				c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
				c.solutionParams[0] = 'thick';
			}
			else
			{
				// Inner is 'thick', need to change it to outer - 1px
				var outerPx = Number(outerWidth.match(this.pxRx)[1]);
				if(outerPx == 1)
				{
					// cant be thiner, go hardcoded
					c.solutions[0] = 'setboth';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = new Array();
					c.solutionParams[0]['inner'] = 'thin';
					c.solutionParams[0]['outer'] = 'medium';
				}
				else
				{
					c.solutions[0] = 'setinner';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = (outerPx - 1) + 'px';
				}
			}
			c.errorMsg += " " + tinyMCEPopup.editor.getLang('stylelibrary_dlg.conflict_outer_cant_compare');
			
			// Add only if valid solution was created.
			if(c.solutions[0])
			{
				this.conflicts[this.conflicts.length] = c;
			}
			
		}
		else if(this.pxRx.test(innerWidth) && this.pxRx.test(outerWidth))
		{
			// Both are pixel
			var innerPx = Number(innerWidth.match(this.pxRx)[1]);
			var outerPx = Number(outerWidth.match(this.pxRx)[1]);
			if(outerPx <= innerPx)
			{
				// Conflict, set outer = inner + 1
				var c = new borderConflict();
				c.type = "outerborder";
				c.rule1 = outerRule;
				c.side1 = outerSide;
				c.rule2 = innerRule;
				c.side2 = innerSide;
				c.errorMsg = errorMsg;
				c.solutions[0] = 'setouter';
				c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
				c.solutionParams[0] = (innerPx + 1) + 'px';
				this.conflicts[this.conflicts.length] = c;
			}
		}
		else
		{
			// None is pixel 
			// Accept or make thin->medium->thick (or slient ignore)
			
			var ok = false;
			if(innerWidth == 'thin' && (outerWidth == 'medium' || outerWidth == 'thick'))
			{
				ok = true;
			}
			if(innerWidth == 'medium' && outerWidth == 'thick')
			{
				ok = true;
			}

			if(!ok)
			{
				var c = new borderConflict();
				c.type = "outerborder";
				c.rule1 = outerRule;
				c.side1 = outerSide;
				c.rule2 = innerRule;
				c.side2 = innerSide;
				c.errorMsg = errorMsg;

				if(innerWidth == 'thin')
				{
					// Make outer wider
					c.solutions[0] = 'setouter';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = 'medium';
				}
				else if(innerWidth == 'medium')
				{
					// Make outer wider
					c.solutions[0] = 'setouter';
					c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
					c.solutionParams[0] = 'thick';
				}
				else
				{
					if(outerWidth == 'thick') 
					{
						// Make inner thiner
						c.solutions[0] = 'setinner';
						c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
						c.solutionParams[0] = 'medium';
					}
					else if(outerWidth == 'medium')
					{
						// Make inner thiner
						// Double border must not be thined
						if(innerStyle == 'double')
						{
							c.solutions[0] = 'setboth';
							c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
							c.solutionParams[0] = new Array();
							c.solutionParams[0]['inner'] = 'medium';
							c.solutionParams[0]['outer'] = 'thick';
						}
						else
						{
							c.solutions[0] = 'setinner';
							c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
							c.solutionParams[0] = 'thin';
						}
					}
					else 
					{
						// Cant be thiner, go hardcoded
						c.solutions[0] = 'setboth';
						c.solutionNames[0] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_correct_width');
						c.solutionParams[0] = new Array();
						c.solutionParams[0]['inner'] = 'thin';
						c.solutionParams[0]['outer'] = 'medium';
					}
				}
				
				// Add only if valid solution was created.
				if(c.solutions[0])
				{
					this.conflicts[this.conflicts.length] = c;
				}
			}
		}
		
		// If created a conflict, add a copy-style solution too
		if(currentConflicts != this.conflicts.length)
		{
			var i = this.conflicts.length - 1; // last conflict
			var s = this.conflicts[i].solutions.length; // place after last solution
			this.conflicts[i].solutions[s] = 'copystyle';
			this.conflicts[i].solutionNames[s] = tinyMCEPopup.editor.getLang('stylelibrary_dlg.solution_copy_inner_style');
		}
	}

	this.resolve = function(conflictIndex, solution) 
	{
		switch(this.conflicts[conflictIndex].type)
		{
			case "gaps":
				this.resolveBorderGaps(this.conflicts[conflictIndex], solution);
				break;
			case "outerborder":
				this.resolveOuterBorder(this.conflicts[conflictIndex], solution);
				break;
		}
	}

	// Resolve a conflict with border gaps (use solid border or remove background color)		
	this.resolveBorderGaps = function(c, solution)
	{
		if(solution == 'nobg')
		{
			c.rule2.setProperty('background-color', '');
		}
		else if(solution == 'nogaps')
		{
			c.rule1.setProperty('border-' + c.side1 + '-style', 'solid');
		}
	}
	
	// Resolve a conflict with outer border (make it wider than inner)
	this.resolveOuterBorder = function(c, solution)
	{
		if(solution == 'setouter')
		{
			c.rule1.setProperty('border-' + c.side1 + '-width', c.solutionParams[0]);
		}
		else if(solution == 'setinner')
		{
			c.rule2.setProperty('border-' + c.side2 + '-width', c.solutionParams[0]);
		}
		else if(solution == 'setboth')
		{
			c.rule1.setProperty('border-' + c.side1 + '-width', c.solutionParams[0]['outer']);
			c.rule2.setProperty('border-' + c.side2 + '-width', c.solutionParams[0]['inner']);
		}
		else if(solution == 'copystyle')
		{
			c.rule1.setProperty('border-' + c.side1 + '-width', c.rule2.getProperty('border-' + c.side2 + '-width'));
			c.rule1.setProperty('border-' + c.side1 + '-style', c.rule2.getProperty('border-' + c.side2 + '-style'));
			c.rule1.setProperty('border-' + c.side1 + '-color', c.rule2.getProperty('border-' + c.side2 + '-color'));
		}
	}
	
	// Render all found conflits into the output panel.
	this.render = function()
	{
		var fldConflict = document.getElementById('fldConflict');
		var txtConflict = document.getElementById('txtConflict');
		
		txtConflict.innerHTML = "";
		
		var html = "";
		for(var i = 0; i < this.conflicts.length; i++)
		{
			html += '<p>' + this.conflicts[i].errorMsg + '<br/>' + this.renderSolutions(i) + '</p>';
			
			if(i < this.conflicts.length - 1)
			{
				html += '<hr/>';
			}
		}
		
		txtConflict.innerHTML = html;
		fldConflict.style.display = (html == '' ? 'none' : 'block');
	}
	this.renderSolutions = function(conflictIndex)
	{
		var sols = "";
		var c = this.conflicts[conflictIndex];
		
		for(var i = 0; i < c.solutions.length; i++)
		{
			sols += ' <input type="button" value="' + 
				c.solutionNames[i] + 
				'" onclick="conflictManager.resolve(' + conflictIndex + ',\'' + c.solutions[i] + '\'); updateDemo(); taskChanged(); return false;" />';
		}
		return sols;
	}
}

function borderConflict()
{
	this.type = '';
	this.rule1 = null;
	this.rule2 = null;
	this.side1 = '';
	this.side2 = '';
	this.errorMsg = '';
	this.solutions = new Array();
	this.solutionNames = new Array();
	this.solutionParams = new Array();
}

// Event handlers for any changes in the form controls (for "auto-update" of backend data and preview)

// User changed border properties, save from form
function borderPicked()
{
	if(BorderPicker.getStyle() =='none') {
		BorderPicker.clear(true)
	};
	if(BorderPicker.validate())
	{
		BorderPicker.saveToRule();
		updateDemo();
	}
}

// User changed font properties, save from form
function fontPicked()
{
	if(fntFont.validate())
	{
		// Save properties from edit panel.
		fntFont.saveToRule();
		updateDemo();
	}
}

// User changed box properties, save from form
function cellPropPicked()
{
	if(cellProp.validate())
	{
		cellProp.saveToRule();
		updateDemo();
	}
}

// User changed margin properties, save from form
function marginPicked()
{
	if(mrgMargin.validate())
	{
		mrgMargin.saveToRule();
		updateDemo();
	}
}
